using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;

namespace EstimationStudio.Core
{
	/// <summary>
	///  The EstimateWriter class provides the facility to save an estimate.
	/// </summary>
	public class EstimateWriter
	{
		private XmlDocument CreateXmlDocument (Estimate estimate)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.AppendChild(xmlDocument.CreateXmlDeclaration("1.0", null, null));

			XmlElement xmlEstimate = CreateElement(xmlDocument, null, "Estimate");

			CreateAttribute(xmlDocument, xmlEstimate, "EstimateId", estimate.EstimateId);
			CreateAttribute(xmlDocument, xmlEstimate, "DateEstimated", estimate.DateEstimated);
			CreateAttribute(xmlDocument, xmlEstimate, "Name", estimate.Title);
			CreateAttribute(xmlDocument, xmlEstimate, "Description", estimate.ProblemDescription);
			CreateAttribute(xmlDocument, xmlEstimate, "SolutionNotes", estimate.SolutionNotes);
			
			SaveProjectPhase(estimate.ProjectPhase, xmlDocument, xmlEstimate);
			SaveAdjustmentFactors(estimate.AdjustmentFactors, xmlDocument, xmlEstimate);
			SaveFeatures(estimate.Features, xmlDocument, xmlEstimate);
			SaveEstimateResult(estimate, xmlDocument, xmlEstimate);

			return xmlDocument;
		}

		private XmlElement CreateXmlElement (Estimate estimate)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.AppendChild(xmlDocument.CreateXmlDeclaration("1.0", null, null));

			XmlElement xmlEstimate = CreateElement(xmlDocument, null, "Estimate");

			CreateAttribute(xmlDocument, xmlEstimate, "Name", estimate.Title);
			CreateAttribute(xmlDocument, xmlEstimate, "Description", estimate.ProblemDescription);

			SaveProjectPhase(estimate.ProjectPhase, xmlDocument, xmlEstimate);
			SaveAdjustmentFactors(estimate.AdjustmentFactors, xmlDocument, xmlEstimate);
			SaveFeatures(estimate.Features, xmlDocument, xmlEstimate);

			return xmlEstimate;
		}

		private void SaveProjectPhase (ProjectPhase projectPhase, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlProjectPhase = CreateElement(xmlDocument, xmlParent, "ProjectPhase");
			CreateAttribute(xmlDocument, xmlProjectPhase, "Name", projectPhase.Name);
			CreateAttribute(xmlDocument, xmlProjectPhase, "ErrorVariability.Low", projectPhase.ErrorVariability.Low);
			CreateAttribute(xmlDocument, xmlProjectPhase, "ErrorVariability.High", projectPhase.ErrorVariability.High);
		}

		private void SaveAdjustmentFactors (IList<AdjustmentFactor> adjustmentFactors, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlAdjustmentFactors = CreateElement(xmlDocument, xmlParent, "AdjustmentFactors");

			foreach (AdjustmentFactor adjustmentFactor in adjustmentFactors)
				SaveAdjustmentFactor(adjustmentFactor, xmlDocument, xmlAdjustmentFactors);
		}

		private void SaveAdjustmentFactor (AdjustmentFactor adjustmentFactor, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlAdjustmentFactor = CreateElement(xmlDocument, xmlParent, "AdjustmentFactor");
			xmlParent.AppendChild(xmlAdjustmentFactor);

			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Name", adjustmentFactor.Name);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Description", adjustmentFactor.Description);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "CurrentRating", adjustmentFactor.CurrentRating.ToString());
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Rating.VeryLow", adjustmentFactor.AdjustmentVariability[Rating.VeryLow]);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Rating.Low", adjustmentFactor.AdjustmentVariability[Rating.Low]);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Rating.High", adjustmentFactor.AdjustmentVariability[Rating.High]);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Rating.VeryHigh", adjustmentFactor.AdjustmentVariability[Rating.VeryHigh]);
			CreateAttribute(xmlDocument, xmlAdjustmentFactor, "Rating.ExtraHigh", adjustmentFactor.AdjustmentVariability[Rating.ExtraHigh]);
		}

		private void SaveFeatures (IList<Feature> features, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlFeatures = CreateElement(xmlDocument, xmlParent, "Features");

			foreach (Feature feature in features)
				SaveFeature(feature, xmlDocument, xmlFeatures);
		}

		private void SaveFeature (Feature feature, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlFeature = CreateElement(xmlDocument, xmlParent, "Feature");
			xmlParent.AppendChild(xmlFeature);

			CreateAttribute(xmlDocument, xmlFeature, "Name", feature.Name);
			CreateAttribute(xmlDocument, xmlFeature, "Description", feature.Description);
			CreateAttribute(xmlDocument, xmlFeature, "SolutionNotes", feature.SolutionNotes);
			CreateAttribute(xmlDocument, xmlFeature, "BestCase", feature.BestCase);
			CreateAttribute(xmlDocument, xmlFeature, "MostLikelyCase", feature.NominalCase);
			CreateAttribute(xmlDocument, xmlFeature, "WorstCase", feature.WorstCase);
		}

		private void SaveEstimateResult (Estimate estimate, XmlDocument xmlDocument, XmlElement xmlParent)
		{
			XmlElement xmlEstimate = CreateElement(xmlDocument, xmlParent, "EstimateResult");
			xmlParent.AppendChild(xmlEstimate);

			CreateAttribute(xmlDocument, xmlEstimate, "BestLow", estimate.BestCase.Low);
			CreateAttribute(xmlDocument, xmlEstimate, "BestHigh", estimate.BestCase.High);
			CreateAttribute(xmlDocument, xmlEstimate, "ExpectedLow", estimate.ExpectedCase.Low);
			CreateAttribute(xmlDocument, xmlEstimate, "ExpectedHigh", estimate.ExpectedCase.High);
			CreateAttribute(xmlDocument, xmlEstimate, "WorstLow", estimate.WorstCase.Low);
			CreateAttribute(xmlDocument, xmlEstimate, "WorstHigh", estimate.WorstCase.High);
		}

		private XmlElement CreateElement (XmlDocument xmlDocument, XmlElement parentElement, String name)
		{
			XmlElement xmlElement = xmlDocument.CreateElement(name);

			if (parentElement == null)
				xmlDocument.AppendChild(xmlElement);
			else
				parentElement.AppendChild(xmlElement);

			return xmlElement;
		}

		private void CreateAttribute (XmlDocument xmlDocument, XmlElement xmlElement, String name, Object value)
		{
			XmlAttribute xmlAttribute = xmlDocument.CreateAttribute(name);
			xmlElement.Attributes.Append(xmlAttribute);
			xmlAttribute.Value = value.ToString();
		}

		/// <summary>
		///  Saves the specified estimate to a file.
		/// </summary>
		/// <param name="estimate">The estimate to save.</param>
		/// <param name="path">The path to save the estimate to.</param>
		public void Save (Estimate estimate, String path)
		{
			CreateXmlDocument(estimate).Save(path);
		}

		/// <summary>
		/// Saves the specified estimate to an XElement.
		/// </summary>
		/// <param name="estimate">The estimate to save.</param>
		/// <returns></returns>
		public XElement SaveAsXml (Estimate estimate)
		{
			XDocument xDocument = XDocument.Parse(CreateXmlDocument(estimate).OuterXml);
			return xDocument.Element("Estimate");
		}
	}
}